home *** CD-ROM | disk | FTP | other *** search
/ CICA 1995 August / CICA - The Ultimate Collection of Shareware for Windows (Disc 2) (August 1995).iso / disc2 / nt / source.exe / POSIX / ELVIS / TMP.C < prev    next >
C/C++ Source or Header  |  1993-07-08  |  13KB  |  594 lines

  1. /* tmpfile.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains functions which create & readback a TMPFILE */
  12.  
  13.  
  14. #include "config.h"
  15. #include <ctype.h>
  16. #include "vi.h"
  17. #if TOS
  18. # include <stat.h>
  19. #else
  20. # if OSK
  21. #  include "osk.h"
  22. # else
  23. #  include <sys/stat.h>
  24. # endif
  25. #endif
  26.  
  27.  
  28. #ifndef NO_MODELINE
  29. static void do_modeline(l, stop)
  30.     long    l;    /* line number to start at */
  31.     long    stop;    /* line number to stop at */
  32. {
  33.     char    *str;    /* used to scan through the line */
  34.     char    *start;    /* points to the start of the line */
  35.     char    buf[80];
  36.  
  37.     /* if modelines are disabled, then do nothing */
  38.     if (!*o_modeline)
  39.     {
  40.         return;
  41.     }
  42.  
  43.     /* for each line... */
  44.     for (l = 1; l <= stop; l++)
  45.     {
  46.         /* for each position in the line.. */
  47.         for (str = fetchline(l); *str; str++)
  48.         {
  49.             /* if it is the start of a modeline command... */
  50.             if ((str[0] == 'e' && str[1] == 'x'
  51.               || str[0] == 'v' && str[1] == 'i')
  52.               && str[2] == ':')
  53.             {
  54.                 start = str += 3;
  55.  
  56.                 /* find the end */
  57.                 while (*str && *str != ':')
  58.                 {
  59.                     str++;
  60.                 }
  61.  
  62.                 /* if it is a well-formed modeline, execute it */
  63.                 if (*str && str - start < sizeof buf)
  64.                 {
  65.                     strncpy(buf, start, (int)(str - start));
  66.                     buf[str - start] = '\0';
  67.                     doexcmd(buf);
  68.                     break;
  69.                 }
  70.             }
  71.         }
  72.     }
  73. }
  74. #endif
  75.  
  76.  
  77. /* The FAIL() macro prints an error message and then exits. */
  78. #define FAIL(why,arg)    mode = MODE_EX; msg(why, arg); endwin(); exit(9)
  79.  
  80. /* This is the name of the temp file */
  81. static char    tmpname[80];
  82.  
  83. /* This function creates the temp file and copies the original file into it.
  84.  * Returns if successful, or stops execution if it fails.
  85.  */
  86. int tmpstart(filename)
  87.     char        *filename; /* name of the original file */
  88. {
  89.     int        origfd;    /* fd used for reading the original file */
  90.     struct stat    statb;    /* stat buffer, used to examine inode */
  91.     REG BLK        *this;    /* pointer to the current block buffer */
  92.     REG BLK        *next;    /* pointer to the next block buffer */
  93.     int        inbuf;    /* number of characters in a buffer */
  94.     int        nread;    /* number of bytes read */
  95.     REG int        j, k;
  96.     int        i;
  97.     int        sum;    /* used for calculating a checksum for this */
  98.     char        *scan;
  99.     long        nbytes;
  100.  
  101.     /* switching to a different file certainly counts as a change */
  102.     changes++;
  103.     redraw(MARK_UNSET, FALSE);
  104.  
  105.     /* open the original file for reading */
  106.     *origname = '\0';
  107.     if (filename && *filename)
  108.     {
  109.         strcpy(origname, filename);
  110.         origfd = open(origname, O_RDONLY);
  111.         if (origfd < 0 && errno != ENOENT)
  112.         {
  113.             msg("Can't open \"%s\"", origname);
  114.             return tmpstart("");
  115.         }
  116.         if (origfd >= 0)
  117.         {
  118.             if (stat(origname, &statb) < 0)
  119.             {
  120.                 FAIL("Can't stat \"%s\"", origname);
  121.             }
  122. #if TOS
  123.             if (origfd >= 0 && (statb.st_mode & S_IJDIR))
  124. #else
  125. # if OSK
  126.             if (origfd >= 0 && (statb.st_mode & S_IFDIR))
  127. # else
  128.             if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
  129. # endif
  130. #endif
  131.             {
  132.                 msg("\"%s\" is not a regular file", origname);
  133.                 return tmpstart("");
  134.             }
  135.         }
  136.         else
  137.         {
  138.             stat(".", &statb);
  139.         }
  140.         if (origfd >= 0)
  141.         {
  142.             origtime = statb.st_mtime;
  143. #if MSDOS || OSK
  144.             if (*o_readonly || !(statb.st_mode & S_IWRITE))
  145. #endif
  146. #if TOS
  147.             if (*o_readonly || (statb.st_mode & S_IJRON))
  148. #endif
  149. #if ANY_UNIX || DF_POSIX
  150.             if (*o_readonly || !(statb.st_mode &
  151.                   (statb.st_uid != geteuid() ? 0022 : 0200)))
  152. #endif
  153.             {
  154.                 setflag(file, READONLY);
  155.             }
  156.         }
  157.         else
  158.         {
  159.             origtime = 0L;
  160.         }
  161.     }
  162.     else
  163.     {
  164.         setflag(file, NOFILE);
  165.         origfd = -1;
  166.         origtime = 0L;
  167.         stat(".", &statb);
  168.     }
  169.  
  170.     /* generate a checksum from the file's name */
  171.     for (sum = 0, scan = origname + strlen(origname);
  172.          --scan >= origname && (isascii(*scan) && isalnum(*scan) || *scan == '.');
  173.          sum = sum + *scan)
  174.     {
  175.     }
  176.     sum &= 0xf;
  177.  
  178.     /* make a name for the tmp file */
  179. #if MSDOS || TOS
  180.     /* MS-Dos doesn't allow multiple slashes, but supports drives
  181.      * with current directories.
  182.      * This relies on TMPNAME beginning with "%s\\"!!!!
  183.      */
  184.     strcpy(tmpname, o_directory);
  185.     if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
  186.         tmpname[i++]=SLASH;
  187.     sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev);
  188. #else
  189.     sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev);
  190. #endif
  191.  
  192. //printf("tmpname=%s, origname=%s, filename=%s|\n",
  193. //    tmpname, origname, filename);
  194.     /* make sure nobody else is editing the same file */
  195.     if (access(tmpname, 0) == 0)
  196.     {
  197.         if (*origname)
  198.         {
  199.             msg("\"%s\" is busy", filename);
  200.             return tmpstart("");
  201.         }
  202.         FAIL("\"%s\" is busy", filename);
  203.     }
  204.  
  205.     /* create the temp file */
  206. #if ANY_UNIX || DF_POSIX || WIN_NT
  207.     close(tmpfd=creat(tmpname, 0600));    /* only we can read it */
  208. #else
  209.     close(creat(tmpname, FILEPERMS));    /* anybody body can read it, alas */
  210. #endif
  211.     tmpfd = open(tmpname, O_RDWR | O_BINARY);
  212.     if (tmpfd < 0)
  213.     {
  214.         FAIL("Can't create temporary file, errno=%d", errno);
  215.         return 1;
  216.     }
  217.  
  218.     /* allocate space for the header in the file */
  219.     write(tmpfd, hdr.c, (unsigned)BLKSIZE);
  220.  
  221. #ifndef NO_RECYCLE
  222.     /* initialize the block allocator */
  223.     /* This must already be done here, before the first attempt
  224.      * to write to the new file! GB */
  225.     garbage();
  226. #endif
  227.  
  228.     /* initialize lnum[] */
  229.     for (i = 1; i < MAXBLKS; i++)
  230.     {
  231.         lnum[i] = INFINITY;
  232.     }
  233.     lnum[0] = 0;
  234.  
  235.     /* if there is no original file, then create a 1-line file */
  236.     if (origfd < 0)
  237.     {
  238.         hdr.n[0] = 0;    /* invalid inode# denotes new file */
  239.  
  240.         this = blkget(1);     /* get the new text block */
  241.         strcpy(this->c, "\n");    /* put a line in it */
  242.  
  243.         lnum[1] = 1L;    /* block 1 ends with line 1 */
  244.         nlines = 1L;    /* there is 1 line in the file */
  245.         nbytes = 1L;
  246.  
  247.         if (*origname)
  248.         {
  249.             msg("\"%s\" [NEW FILE]  1 line, 1 char", origname);
  250.         }
  251.         else
  252.         {
  253.             msg("\"[NO FILE]\"  1 line, 1 char");
  254.         }
  255.     }
  256.     else /* there is an original file -- read it in */
  257.     {
  258.         hdr.n[0] = statb.st_ino;
  259.         nbytes = nlines = 0;
  260.  
  261.         /* preallocate 1 "next" buffer */
  262.         i = 1;
  263.         next = blkget(i);
  264.         inbuf = 0;
  265.  
  266.         /* loop, moving blocks from orig to tmp */
  267.         for (;;)
  268.         {
  269.             /* "next" buffer becomes "this" buffer */
  270.             this = next;
  271.  
  272.             /* read [more] text into this block */
  273.             nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
  274.             if (nread < 0)
  275.             {
  276.                 close(origfd);
  277.                 close(tmpfd);
  278.                 tmpfd = -1;
  279.                 unlink(tmpname);
  280.                 FAIL("Error reading \"%s\"", origname);
  281.             }
  282.  
  283.             /* convert NUL characters to something else */
  284.             for (k = inbuf; k < inbuf + nread; k++)
  285.             {
  286.                 if (!this->c[k])
  287.                 {
  288.                     setflag(file, HADNUL);
  289.                     this->c[k] = 0x80;
  290.                 }
  291.             }
  292.             inbuf += nread;
  293.  
  294.             /* if the buffer is empty, quit */
  295.             if (inbuf == 0)
  296.             {
  297.                 goto FoundEOF;
  298.             }
  299.  
  300. #if MSDOS || TOS
  301. /* BAH! MS text mode read fills inbuf, then compresses eliminating \r
  302.    but leaving garbage at end of buf. The same is true for TURBOC. GB. */
  303.  
  304.             memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
  305. #endif
  306.  
  307.             /* search backward for last newline */
  308.             for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
  309.             {
  310.             }
  311.             if (k++ < 0)
  312.             {
  313.                 if (inbuf >= BLKSIZE - 1)
  314.                 {
  315.                     k = 80;
  316.                 }
  317.                 else
  318.                 {
  319.                     k = inbuf;
  320.                 }
  321.             }
  322.  
  323.             /* allocate next buffer */
  324.             next = blkget(++i);
  325.  
  326.             /* move fragmentary last line to next buffer */
  327.             inbuf -= k;
  328.             for (j = 0; k < BLKSIZE; j++, k++)
  329.             {
  330.                 next->c[j] = this->c[k];
  331.                 this->c[k] = 0;
  332.             }
  333.  
  334.             /* if necessary, add a newline to this buf */
  335.             for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
  336.             {
  337.             }
  338.             if (this->c[k] != '\n')
  339.             {
  340.                 setflag(file, ADDEDNL);
  341.                 this->c[k + 1] = '\n';
  342.             }
  343.  
  344.             /* count the lines in this block */
  345.             for (k = 0; k < BLKSIZE && this->c[k]; k++)
  346.             {
  347.                 if (this->c[k] == '\n')
  348.                 {
  349.                     nlines++;
  350.                 }
  351.                 nbytes++;
  352.             }
  353.             lnum[i - 1] = nlines;
  354.         }
  355. FoundEOF:
  356.  
  357.         /* if this is a zero-length file, add 1 line */
  358.         if (nlines == 0)
  359.         {
  360.             this = blkget(1);     /* get the new text block */
  361.             strcpy(this->c, "\n");    /* put a line in it */
  362.  
  363.             lnum[1] = 1;    /* block 1 ends with line 1 */
  364.             nlines = 1;    /* there is 1 line in the file */
  365.             nbytes = 1;
  366.         }
  367.  
  368. #if MSDOS || TOS
  369.         /* each line has an extra CR that we didn't count yet */
  370.         nbytes += nlines;
  371. #endif
  372.  
  373.         /* report the number of lines in the file */
  374.         msg("\"%s\" %s %ld line%s, %ld char%s",
  375.             origname,
  376.             (tstflag(file, READONLY) ? "[READONLY]" : ""),
  377.             nlines,
  378.             nlines == 1 ? "" : "s",
  379.             nbytes,
  380.             nbytes == 1 ? "" : "s");
  381.     }
  382.  
  383.     /* initialize the cursor to start of line 1 */
  384.     cursor = MARK_FIRST;
  385.  
  386.     /* close the original file */
  387.     close(origfd);
  388.  
  389.     /* any other messages? */
  390.     if (tstflag(file, HADNUL))
  391.     {
  392.         msg("This file contained NULs.  They've been changed to \\x80 chars");
  393.     }
  394.     if (tstflag(file, ADDEDNL))
  395.     {
  396.         msg("Newline characters have been inserted to break up long lines");
  397.     }
  398.  
  399. #ifndef NO_MODELINE
  400.     if (nlines > 10)
  401.     {
  402.         do_modeline(1L, 5L);
  403.         do_modeline(nlines - 4L, nlines);
  404.     }
  405.     else
  406.     {
  407.         do_modeline(1L, nlines);
  408.     }
  409. #endif
  410.     return 0;
  411. }
  412.  
  413.  
  414.  
  415. /* This function copies the temp file back onto an original file.
  416.  * Returns TRUE if successful, or FALSE if the file could NOT be saved.
  417.  */
  418. int tmpsave(filename, bang)
  419.     char    *filename;    /* the name to save it to */
  420.     int    bang;        /* forced write? */
  421. {
  422.     int        fd;    /* fd of the file we're writing to */
  423.     REG int        len;    /* length of a text block */
  424.     REG BLK        *this;    /* a text block */
  425.     long        bytes;    /* byte counter */
  426.     REG int        i;
  427.  
  428.     /* if no filename is given, assume the original file name */
  429.     if (!filename || !*filename)
  430.     {
  431.         filename = origname;
  432.     }
  433.  
  434.     /* if still no file name, then fail */
  435.     if (!*filename)
  436.     {
  437.         msg("Don't know a name for this file -- NOT WRITTEN");
  438.         return FALSE;
  439.     }
  440.  
  441.     /* can't rewrite a READONLY file */
  442.     if (!strcmp(filename, origname) && *o_readonly && !bang)
  443.     {
  444.         msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
  445.         return FALSE;
  446.     }
  447.  
  448.     /* open the file */
  449.     if (*filename == '>' && filename[1] == '>')
  450.     {
  451.         filename += 2;
  452.         while (*filename == ' ' || *filename == '\t')
  453.         {
  454.             filename++;
  455.         }
  456. #ifdef O_APPEND
  457.         fd = open(filename, O_WRONLY|O_APPEND);
  458. #else
  459.         fd = open(filename, O_WRONLY);
  460.         lseek(fd, 0L, 2);
  461. #endif
  462.     }
  463.     else
  464.     {
  465.         /* either the file must not exist, or it must be the original
  466.          * file, or we must have a bang
  467.          */
  468.         if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang)
  469.         {
  470.             msg("File already exists - Use :w! to overwrite");
  471.             return FALSE;
  472.         }
  473.         fd = creat(filename, FILEPERMS);
  474.     }
  475.     if (fd < 0)
  476.     {
  477.         msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
  478.         return FALSE;
  479.     }
  480.  
  481.     /* write each text block to the file */
  482.     bytes = 0L;
  483.     for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
  484.     {
  485.         for (len = 0; len < BLKSIZE && this->c[len]; len++)
  486.         {
  487.         }
  488.         twrite(fd, this->c, len);
  489.         bytes += len;
  490.     }
  491.  
  492.     /* reset the "modified" flag */
  493.     clrflag(file, MODIFIED);
  494.     significant = FALSE;
  495.  
  496.     /* report lines & characters */
  497. #if MSDOS || TOS
  498.     bytes += nlines; /* for the inserted carriage returns */
  499. #endif
  500.     if (strncmp(filename, o_directory, strlen(o_directory)))
  501.     {
  502.         msg("Wrote \"%s\"  %ld lines, %ld characters", filename, nlines, bytes);
  503.     }
  504.  
  505.     /* close the file */
  506.     close(fd);
  507.  
  508.     return TRUE;
  509. }
  510.  
  511.  
  512. /* This function deletes the temporary file.  If the file has been modified
  513.  * and "bang" is FALSE, then it returns FALSE without doing anything; else
  514.  * it returns TRUE.
  515.  *
  516.  * If the "autowrite" option is set, then instead of returning FALSE when
  517.  * the file has been modified and "bang" is false, it will call tmpend().
  518.  */
  519. int tmpabort(bang)
  520.     int    bang;
  521. {
  522.     /* if there is no file, return successfully */
  523.     if (tmpfd < 0)
  524.     {
  525.         return TRUE;
  526.     }
  527.  
  528.     /* see if we must return FALSE -- can't quit */
  529.     if (!bang && tstflag(file, MODIFIED))
  530.     {
  531.         /* if "autowrite" is set, then act like tmpend() */
  532.         if (*o_autowrite)
  533.             return tmpend(bang);
  534.         else
  535.             return FALSE;
  536.     }
  537.  
  538.     /* delete the tmp file */
  539.     cutswitch(tmpname);
  540.     close(tmpfd);
  541.     tmpfd = -1;
  542.     unlink(tmpname);
  543.     strcpy(prevorig, origname);
  544.     prevline = markline(cursor);
  545.     *origname = '\0';
  546.     origtime = 0L;
  547.     blkinit();
  548.     nlines = 0;
  549.     initflags();
  550.     return TRUE;
  551. }
  552.  
  553. /* This function saves the file if it has been modified, and then deletes
  554.  * the temporary file. Returns TRUE if successful, or FALSE if the file
  555.  * needs to be saved but can't be.  When it returns FALSE, it will not have
  556.  * deleted the tmp file, either.
  557.  */
  558. int tmpend(bang)
  559.     int    bang;
  560. {
  561.     /* save the file if it has been modified */
  562.     if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
  563.     {
  564.         return FALSE;
  565.     }
  566.  
  567.     /* delete the tmp file */
  568.     tmpabort(TRUE);
  569.  
  570.     return TRUE;
  571. }
  572.  
  573.  
  574. /* If the tmp file has been changed, then this function will force those
  575.  * changes to be written to the disk, so that the tmp file will survive a
  576.  * system crash or power failure.
  577.  */
  578. #if MSDOS || TOS || OSK
  579. sync()
  580. {
  581. # if OSK
  582.     /* OS9 doesn't need an explicit sync operation, but the linker
  583.      * demands something called sync(), so this is a dummy function.
  584.      */
  585. #else
  586.     /* MS-DOS and TOS don't flush their buffers until the file is closed,
  587.      * so here we close the tmp file and then immediately reopen it.
  588.      */
  589.     close(tmpfd);
  590.     tmpfd = open(tmpname, O_RDWR | O_BINARY);
  591. #endif
  592. }
  593. #endif
  594.